package com.smartao.core.orm;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;

/**
 * 排序参数封装
 */
public class Order implements Serializable {

	private static final long serialVersionUID = -1128357365357620520L;

	private static final Direction DEFAULT_DIRECTION = Direction.ASC;

	private final Direction direction;
	private final String property;

	/**
	 * Creates a new {@link Order} instance. if order is {@literal null} then order defaults to
	 * {@value Sort#DEFAULT_DIRECTION}
	 * 
	 * @param direction can be {@code null}
	 * @param property must not be {@code null} or empty
	 */
	public Order(Direction direction, String property) {

		if (property == null || "".equals(property.trim())) {
			throw new IllegalArgumentException("PropertyPath must not null or empty!");
		}

		this.direction = direction == null ? DEFAULT_DIRECTION : direction;
		this.property = property;
	}

	/**
	 * Creates a new {@link Order} instance. Takes a single property. Order defaults to {@value Sort.DEFAULT_ORDER}
	 * 
	 * @param property - must not be {@code null} or empty
	 */
	public Order(String property) {
		this(DEFAULT_DIRECTION, property);
	}

	public static List<Order> create(Direction direction, Iterable<String> properties) {

		List<Order> orders = new ArrayList<Order>();
		for (String property : properties) {
			orders.add(new Order(direction, property));
		}
		return orders;
	}

	/**
	 * Returns the order the property shall be sorted for.
	 * 
	 * @return
	 */
	public Direction getDirection() {
		return direction;
	}

	/**
	 * Returns the property to order for.
	 * 
	 * @return
	 */
	public String getProperty() {
		return property;
	}

	/**
	 * Returns whether sorting for this property shall be ascending.
	 * 
	 * @return
	 */
	public boolean isAscending() {
		return this.direction.equals(Direction.ASC);
	}

	/**
	 * Returns a new {@link Order} with the given {@link Order}.
	 * 
	 * @param order
	 * @return
	 */
	public Order with(Direction order) {
		return new Order(order, this.property);
	}

	/**
	 * Returns a new {@link Sort} instance for the given properties.
	 * 
	 * @param properties
	 * @return
	 */
	public Sort withProperties(String... properties) {
		return new Sort(this.direction, properties);
	}

	/*
	 * (non-Javadoc)
	 * @see java.lang.Object#hashCode()
	 */
	@Override
	public int hashCode() {

		int result = 17;

		result = 31 * result + direction.hashCode();
		result = 31 * result + property.hashCode();

		return result;
	}

	/*
	 * (non-Javadoc)
	 * @see java.lang.Object#equals(java.lang.Object)
	 */
	@Override
	public boolean equals(Object obj) {

		if (this == obj) {
			return true;
		}

		if (!(obj instanceof Order)) {
			return false;
		}

		Order that = (Order) obj;

		return this.direction.equals(that.direction) && this.property.equals(that.property);
	}

	/*
	 * (non-Javadoc)
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString() {
		return String.format("%s: %s", property, direction);
	}
	
	public static enum Direction {

		ASC, DESC;

		/**
		 * Returns the {@link Direction} enum for the given {@link String} value.
		 * 
		 * @param value
		 * @return
		 */
		public static Direction fromString(String value) {

			try {
				return Direction.valueOf(value.toUpperCase(Locale.US));
			} catch (Exception e) {
				throw new IllegalArgumentException(String.format(
						"Invalid value '%s' for orders given! Has to be either 'desc' or 'asc' (case insensitive).", value), e);
			}
		}
	}
}
